#include "network/tcp_connection.hpp"

#include "base/assert.hpp"
#include "base/log.hpp"

namespace ibox::network {

using namespace std::placeholders;

TcpConnection::TcpConnection(event::Loop *loop,
                             SocketFd fd,
                             const SockAddr &peer_addr)
    : m_loop{loop}, m_buffd{std::make_unique<BufferedFd>(loop->ptr())},
      m_peer_addr{peer_addr} {
    // 这里会调用SockAddr的拷贝构造函数, 复制一份, 而不是共用, 没有问题
    m_buffd->initialize(fd);
    m_buffd->set_read_zero_callback([&]() { on_socket_closed(); });
    m_buffd->set_error_callback([&](int errnum) { on_error(errnum); });

    m_buffd->enable();
}

void TcpConnection::enable() { m_buffd->enable(); }

bool TcpConnection::disconnect() {
    LOG_INFO("disconnect {}", m_peer_addr.to_string());
    if (m_buffd == nullptr) {
        return false;
    }

    m_buffd->disable();

    // BufferedFd *tmp = nullptr;
    // std::swap(tmp, m_buffd);

    //// m_loop->runNext([tmp] { CHECK_DELETE_OBJ(tmp); },
    ////                   "TcpConnection::disconnect, delete tmp");

    return true;
}

bool TcpConnection::shutdown(int howto) {
    LOG_INFO("shutdown {}, {}", m_peer_addr.to_string(), howto);
    if (m_buffd == nullptr) {
        return false;
    }

    SocketFd socket_fd(m_buffd->fd());
    return socket_fd.shutdown(howto) == 0;
}

SocketFd TcpConnection::socket_fd() const {
    if (m_buffd != nullptr) {
        return SocketFd(m_buffd->fd());
    }
    return SocketFd();
}

void *TcpConnection::set_context(void *new_context) {
    auto *old_context = m_context;
    m_context = new_context;
    return old_context;
}

void TcpConnection::set_receive_callback(ReceiveCallback cb, size_t threshold) {
    if (m_buffd != nullptr) {
        m_buffd->set_receive_callback(cb, threshold);
    }
}

void TcpConnection::bind(ByteStream *receiver) {
    if (m_buffd != nullptr) {
        m_buffd->bind(receiver);
    }
}

void TcpConnection::unbind() {
    if (m_buffd != nullptr) {
        m_buffd->unbind();
    }
}

bool TcpConnection::send(const void *data_ptr, size_t data_size) {
    if (m_buffd != nullptr) {
        return m_buffd->send(data_ptr, data_size);
    }
    return false;
}

void TcpConnection::on_socket_closed() {
    LOG_INFO("closing connection {}", m_peer_addr.to_string());

    // BufferedFd *tmp = nullptr;
    // std::swap(tmp, m_buffd);

    // m_loop->runNext([tmp] { CHECK_DELETE_OBJ(tmp); },
    //                   "TcpConnection::on_socket_closed, delete tmp");

    LOG_DEBUG("before disconnected callback");
    if (m_disconnected_cb) {
        m_disconnected_cb();
    }
    LOG_DEBUG("after disconnected callback");
}

void TcpConnection::on_error(int errnum) {
    if (errnum == ECONNRESET) {
        on_socket_closed();
    } else {
        LOG_WARNING("errno: {}, {}", errnum, strerror(errnum));
    }
}

} // namespace ibox::network
